﻿using HystereticRegularization;
using PGMHelper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HystereticBasic
{
    /// <summary>
    /// 单调曲线
    /// </summary>
    public class MontForceDispObj: ForceDispObj
    {
        /// <summary>
        /// 峰值点
        /// </summary>
        private ForceDispBasicPair CappingPoint
        {
            get
            {
                if (Math.Abs(this.ForceMinPoint.Force) >
               Math.Abs(this.ForceMaxPoint.Force))
                    return this.ForceMinPoint;
                else return this.ForceMaxPoint;
            }
        }

        /// <summary>
        /// 峰值前曲线
        /// </summary>
        public MontForceDispObj PrePeakSkeleton
        {
            get
            {
                //力最大点
                var forceMaxPt = this.CappingPoint;
                //根据力最大值切为两部分
                var ObjTuple = this.Seperate(forceMaxPt);
                //判断是否存在力最大点
                if (!ObjTuple.Item1.ContainsOrder(forceMaxPt.Order))
                    ObjTuple.Item1.Add(forceMaxPt);
                return ObjTuple.Item1;
            }
        }

        /// <summary>
        /// 峰值后曲线
        /// </summary>
        public MontForceDispObj PostPeakSkeleton
        {
            get
            {
                //力最大点
                var forceMaxPt = this.CappingPoint;
                //根据力最大值切为两部分
                var ObjTuple = this.Seperate(forceMaxPt);
                //判断是否存在力最大点
                if (!ObjTuple.Item2.ContainsOrder(forceMaxPt.Order))
                    ObjTuple.Item2.Insert(0, forceMaxPt);
                return ObjTuple.Item2;
            }
        }

        #region 点插值

        /// <summary>
        /// 获得曲线与曲线的交点
        /// </summary>
        /// <param name="montObj"></param>
        /// <param name="isFontSearch"></param>
        /// <param name="interPt"></param>
        /// <returns></returns>
        public bool GetIntersectionPt(MontForceDispObj montObj, bool isFontSearch, out ForceDispBasicPair interPt)
        {
            //初始化
            interPt = new ForceDispBasicPair();
            //前搜索
            if (isFontSearch)
            {
                for(int i = 1; i < montObj.Count; i++)
                {
                    if (this.GetIntersectionPt(montObj[i - 1], montObj[i], out interPt))
                        return true;
                }
                return false;
            }
            else
            {
                for (int i = montObj.Count - 1; i > 0; i--)
                {
                    if (this.GetIntersectionPt(montObj[i - 1], montObj[i], out interPt))
                        return true;
                }
                return false;
            }
        }

        /// <summary>
        /// 获得曲线与直线的最后一个交点
        /// </summary>
        /// <param name="ptI"> </param>
        /// <param name="ptJ"></param>
        /// <param name="interPt"> 交点 </param>
        /// <returns></returns>
        public bool GetIntersectionPtBack(ForceDispBasicPair ptI, ForceDispBasicPair ptJ, out ForceDispBasicPair interPt)
        {
            //初始化
            interPt = new ForceDispBasicPair();
            //创建相交线
            var crossLine2 = new MontForceDispObj(new List<ForceDispBasicPair> { ptI, ptJ });
            //遍历线
            for (int i = this.Count - 1; i > 0; i--)
            {
                //获得对象
                var obj = this[i];
                var preObj = this[i - 1];
                //创建相交线
                var crossLine1 = new MontForceDispObj(new List<ForceDispBasicPair> { preObj, obj });
                //是否相交
                bool isCross = HystereticHelper.GetIntersectionInLine(crossLine1, crossLine2, out interPt);
                //判断是否相交
                if (!isCross) continue;
                //相交
                else return true;
            }
            return false;
        }

        /// <summary>
        /// 获得曲线与直线的第一交点
        /// </summary>
        /// <param name="ptI"> </param>
        /// <param name="ptJ"></param>
        /// <param name="interPt"> 交点 </param>
        /// <returns></returns>
        public bool GetIntersectionPt(ForceDispBasicPair ptI, ForceDispBasicPair ptJ, out ForceDispBasicPair interPt)
        {
            //初始化
            interPt = new ForceDispBasicPair();
            //创建相交线
            var crossLine2 = new MontForceDispObj(new List<ForceDispBasicPair> { ptI, ptJ } );
            //遍历线
            for (int i = 1; i < this.Count; i++)
            {
                //获得对象
                var obj = this[i];
                var preObj = this[i - 1];
                //创建相交线
                var crossLine1 = new MontForceDispObj(new List<ForceDispBasicPair> { preObj, obj });
                //是否相交
                bool isCross = HystereticHelper.GetIntersectionInLine(crossLine1, crossLine2, out interPt);
                //判断是否相交
                if (!isCross) continue;
                //相交
                else return true;
            }
            return false;
        }

        /// <summary>
        /// 根据力插值点
        /// </summary>
        /// <param name="force"></param>
        /// <param name="interPt"></param>
        /// <returns></returns>
        public bool GetPtByForce(float force, out ForceDispBasicPair interPt)
        {
            //获得曲线位移最大点
            var dispMax = this.DispMaxPoint.Disp * 2;
            var dispMin = this.DispMinPoint.Disp * 2;
            //确定最终区间
            dispMax = dispMax > 0 ? dispMax : 0;
            dispMin = dispMin < 0 ? dispMin : 0;
            //插值
            return this.GetIntersectionPt(new ForceDispBasicPair(dispMin, force),
               new ForceDispBasicPair(dispMax, force), out interPt);
        }

        /// <summary>
        /// 根据力插值点
        /// </summary>
        /// <param name="force"></param>
        /// <param name="isPrePeak"></param>
        /// <returns></returns>
        public bool GetPtByForce(float force, bool isPrePeak, out ForceDispBasicPair interPt)
        {
            //获得对象
            var obj = isPrePeak ? this.PrePeakSkeleton : this.PostPeakSkeleton;
            return obj.GetPtByForce(force, out interPt);
        }

        /// <summary>
        /// 根据位移插值点
        /// </summary>
        /// <param name="force"></param>
        /// <param name="interPt"></param>
        /// <returns></returns>
        public bool GetPtByDisp(float disp, out ForceDispBasicPair interPt)
        {
            //获得曲线位移最大点
            var forceMax = this.ForceMaxPoint.Force * 2;
            var forceMin = this.ForceMinPoint.Force * 2;
            //确定最终区间
            forceMax = forceMax > 0 ? forceMax : 0;
            forceMin = forceMin < 0 ? forceMin : 0;
            //插值
            return this.GetIntersectionPt( new ForceDispBasicPair(disp, forceMin),
               new ForceDispBasicPair(disp, forceMax), out interPt);
        }

        /// <summary>
        /// 根据位移插值点
        /// </summary>
        /// <param name="disp"></param>
        /// <returns></returns>
        public ForceDispBasicPair GetPtByDisp(float disp)
        {
            var pair = new ForceDispBasicPair(disp, 0);
            this.GetPtByDisp(disp, out pair);
            return pair;
        }

        /// <summary>
        /// 根据位移插值点
        /// </summary>
        /// <param name="force"></param>
        /// <param name="interPt"></param>
        /// <returns></returns>
        public bool GetPtByDisp(float disp, bool isPrePeak, out ForceDispBasicPair interPt)
        {
            //获得对象
            var obj = isPrePeak ? this.PrePeakSkeleton : this.PostPeakSkeleton;
            return obj.GetPtByDisp(disp, out interPt);
        }

        #endregion

        /// <summary>
        /// 清空数据
        /// </summary>
        public void Clear()
        {
            this.DataList.Clear();
        }

        /// <summary>
        /// 最大割线刚度
        /// </summary>
        /// <returns></returns>
        public float GetMaxSecantStiffness()
        {
            if (this.Count == 0) return 0;
            return (from obj in this.DataList select obj.SecantStiffness).ToList().Max();
        }

        /// <summary>
        /// 计算包围面积
        /// </summary>
        /// <returns></returns>
        public float GetA(bool isPostPeakBranch)
        {
            return isPostPeakBranch ? this.PrePeakSkeleton.GetEnergy() : this.PostPeakSkeleton.GetEnergy();
        }

        /// <summary>
        /// 力位移曲线位移放缩
        /// </summary>
        /// <param name="factor"> 放缩系数 </param>
        public void DispAmplitude(double factor)
        {
            this.DataList.ForEach(data => data.Disp *= (float)factor);
        }

        /// <summary>
        /// 力位移曲线力放缩
        /// </summary>
        /// <param name="factor"> 放缩系数 </param>
        public void ForceAmplitude(double factor)
        {
            this.DataList.ForEach(data => data.Force *= (float)factor);
        }

        /// <summary>
        /// 根据位移排序
        /// </summary>
        public void OrderByDisp()
        {
            this.DataList.OrderBy("Disp");
        }

        /// <summary>
        /// 是否递增
        /// 仅针对单调的力位移曲线存在意义
        /// </summary>
        /// <returns></returns>
        public bool isASC()
        {
            return this.First().Disp < this.Last().Disp;
        }

        /// <summary>
        /// 是否正向加载
        /// </summary>
        /// <returns></returns>
        public bool isPosLoading()
        {
            return this.Last().Disp > this.First().Disp;
        }

        /// <summary>
        /// 获得数值曲线
        /// </summary>
        /// <returns></returns>
        public MontForceDispObj GetNumericalSkeleton()
        {
            //获得上升段和下降段
            var preCap = this.PrePeakSkeleton;
            var postCap = this.PostPeakSkeleton;
            //曲线代数化
            var preMont = preCap.GetNumericalForceDispObj();
            var PostMont = postCap.GetNumericalForceDispObj();
            //仅加入部分点
            for (int i = 1; i < PostMont.Count; i++)
                preMont.Add(PostMont[i]);
            //返回
            return preMont;
        }

        #region Skeleton and Smooth

        /// <summary>
        /// 调整为等间距数据点
        /// 仅针对单调的力位移曲线存在意义
        /// </summary>
        /// <param name="prtNum"></param>
        /// <returns></returns>
        public MontForceDispObj GetForceDispObjBySameIncreate(int ptCount)
        {
            //计算变形增量
            var deltaDisp = (this.Last().Disp - this.First().Disp) / ptCount;
            //增量存在
            if(Math.Abs(deltaDisp) < GlobalParam.DispTol)
                return new MontForceDispObj();
            //获得离散后的数据
            else return this.GetForceDispObjBySameIncreate(deltaDisp, this.Last());
        }

        /// <summary>
        /// 调整为等间距数据点
        /// 仅针对单调的力位移曲线存在意义
        /// </summary>
        /// <param name="prtNum"></param>
        /// <returns></returns>
        public MontForceDispObj GetForceDispObjBySameIncreate(float deltaDisp, ForceDispBasicPair ptJ)
        {
            //初始化
            var obj = new MontForceDispObj(this.First());
            //初始位移值
            var targetDisp = this.First().Disp + deltaDisp;
            //遍历数据
            for (int index = 1; index < this.Count;)
            {
                //位移增量不存在
                if (Math.Abs(this[index].Disp - this[index - 1].Disp) < GlobalParam.DispTol)
                {
                    index++; continue;
                }
                //目标位移已越界
                if ((this.First().Disp - targetDisp) * (ptJ.Disp - targetDisp) > 0) break;
                //区间内 计算斜率
                var slope = this[index - 1].GetStiffness(this[index]);
                //直至点不在区间内
                while ((this[index].Disp - targetDisp) * (this[index - 1].Disp - targetDisp) <= 0)
                {
                    //确定力
                    var force = this[index - 1].Force + slope * (targetDisp - this[index - 1].Disp);
                    //添加点
                    obj.Add(new ForceDispBasicPair(targetDisp, force));
                    //散点递增
                    targetDisp += deltaDisp;
                }
                index++;
            }
            return obj;
        }

        /// <summary>
        /// 曲线分割
        /// </summary>
        /// <param name="seperatePt"></param>
        /// <returns></returns>
        public Tuple<MontForceDispObj, MontForceDispObj> Seperate(ForceDispBasicPair seperatePt, bool isSeperatePtAdd = false)
        {
            //初始化容器
            var preList = new MontForceDispObj();
            var postList = new MontForceDispObj();
            //获得首个数据点
            var firstPt = this.First();
            //遍历数据
            foreach (var obj in this.DataList)
            {
                if ((obj.Disp - firstPt.Disp) * (obj.Disp - seperatePt.Disp) <= 0)
                    preList.Add(obj);
                else postList.Add(obj);
            }
            //函数指针
            Action<MontForceDispObj, bool> addPt = (montObj, isPre) =>
           {
               //添加插入点
               if (isSeperatePtAdd)
               {
                   if(montObj.Count != 0)
                   {
                       var pt = isPre ? montObj.Last() : montObj.First();
                       //判断位移是否相同
                       if (Math.Abs(pt.Disp - seperatePt.Disp) > GlobalParam.DispTol)
                       {
                           if (isPre) montObj.Add(seperatePt);
                           else montObj.Insert(0, seperatePt);
                       }
                   }
               }
           };
            //插入点
            addPt(preList, true);
            addPt(postList, false);
            return Tuple.Create<MontForceDispObj, MontForceDispObj>(preList, postList);
        }

        /// <summary>
        /// 曲线分割
        /// </summary>
        /// <param name="meshNum"></param>
        /// <returns></returns>
        public List<MontForceDispObj> Seperate(int meshNum)
        {
            //剖分对象
            Func<List<MontForceDispObj>, List<MontForceDispObj>> meshObj = (preMeshObj) =>
            {
                //初始化对象
                var postMeshObj = new List<MontForceDispObj>();
                //遍历现有对象
                foreach (var obj in preMeshObj)
                {
                    //软化段采用二折线拟合
                    var soe = new RegularizedNormalCurveSOE(new MontForceDispObj(obj.ToList()));
                    //定义拐点
                    ForceDispBasicPair breakPt;
                    //获得拐点
                    if (soe.GetBreakPt(out breakPt))
                    {
                        //获得对象
                        var softTuple = obj.Seperate(breakPt);
                        //添加
                        postMeshObj.Add(softTuple.Item1);
                        postMeshObj.Add(softTuple.Item2);
                    }
                    else postMeshObj.Add(obj);
                }
                //返回对象
                return postMeshObj;
            };
            //初始化
            var objList = new List<MontForceDispObj> { this };
            //开启剖分
            for (int i = 0; i < meshNum; i++)
                objList = meshObj(objList);
            //返回对象
            return objList;
        }

        /// <summary>
        /// 根据次序依次添加割线刚度最大的对象
        /// </summary>
        /// <param name="forceDispObj"></param>
        /// <param name="isPos"></param>
        /// <param name="stiffLimit"></param>
        public void AddMaxStiffObj(MontForceDispObj forceDispObj, bool isPos, double stiffLimit = -1)
        {
            //初始点序号
            int startIndex = -1;
            //获得当前最大变形点
            var skeletonDispMax = isPos ? this.DispMaxPoint : this.DispMinPoint;
            //未到最后的点
            while (forceDispObj.Count > startIndex++)
            {
                //构造字典
                var stiffDict = new SortedDictionary<double, int>();
                //遍历数据点计算刚度
                for (int subIndex = startIndex; subIndex < forceDispObj.Count; subIndex++)
                {
                    //获得当前点
                    var curPt = forceDispObj[subIndex];
                    //大于历史最大变形点
                    if (isPos ? skeletonDispMax.Disp > curPt.Disp : skeletonDispMax.Disp < curPt.Disp)
                        continue;
                    //防止刚度无限大
                    if (Math.Abs(skeletonDispMax.Disp - curPt.Disp) < GlobalParam.DispTol) continue;
                    //计算刚度
                    var stiffness = skeletonDispMax.GetStiffness(curPt);
                    //是否有限值
                    if (stiffLimit > 0 && stiffness > stiffLimit) continue;
                    //添加对象
                    if (!stiffDict.ContainsKey(stiffness))
                        stiffDict.Add(stiffness, subIndex);
                }
                //不存在对象
                if (stiffDict.Count == 0) break;
                //存在对象
                startIndex = stiffDict.Last().Value;
                //添加对象
                this.Add(new ForceDispBasicPair(forceDispObj[startIndex]));
                //获得当前最大变形点
                skeletonDispMax = isPos ? this.DispMaxPoint : this.DispMinPoint;
            }
        }

        /// <summary>
        /// 曲线代数化
        /// </summary>
        /// <returns></returns>
        public MontForceDispObj GetNumericalForceDispObj()
        {
            //构造新曲线
            var forceDispObj = new MontForceDispObj();
            //是否存在数据点
            if (this.Count == 0) return forceDispObj;
            //添加初始点
            forceDispObj.Add(this.First());
            //二折线拟合
            var soe = new RegularizedNormalCurveSOE(new MontForceDispObj(this.ToList()));
            //定义拐点
            ForceDispBasicPair breakPt;
            //获得拐点
            if (soe.GetBreakPt(out breakPt)) forceDispObj.Add(breakPt);
            //添加结束点
            forceDispObj.Add(this.Last());
            return forceDispObj;
        }

        #endregion

        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        public MontForceDispObj()
            :base()
        { 
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="startPt"></param>
        public MontForceDispObj(ForceDispBasicPair startPt)
               : base()
        {
            this.Add(startPt);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="skipLine"></param>
        public MontForceDispObj(string filePath, int skipLine)
            :base(filePath, skipLine)
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="originalcurve"></param>
        public MontForceDispObj(List<ForceDispBasicPair> originalcurve)
             : base(originalcurve)
        {

        }

        /// <summary>
        /// 复制对象
        /// </summary>
        /// <param name="obj"></param>
        public MontForceDispObj(MontForceDispObj obj)
            :base(obj)
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dispList"></param>
        /// <param name="forceList"></param>
        public MontForceDispObj(List<float> dispList, List<float> forceList)
            :base(dispList, forceList)
        {

        }

        #endregion
    }
}
